Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minimal uiTable implementation for windows #361

Merged
merged 5 commits into from
May 30, 2018

Conversation

bcampbell
Copy link
Contributor

(The first two commits are cherry-picked from the msys fixes - I needed them to build. The table changes are in a single commit)

This uses the win32 common controls listview to implement uiTable.
There are limitations:

  • It supports only a single TextPart per column.
  • ImagePart, CheckboxPart and ProgessBarPart are not implemented.
  • There is no support for cell coloring.
  • Cell editing is not implemented.
    Some of these will be very hard to support using the standard
    common control listview, and probably require an entire custom
    listview.

@msink
Copy link
Contributor

msink commented May 5, 2018

msys2 build is fixed on master branch - so maybe we need to merge andlabs:master into andlabs:table first.

@andlabs
Copy link
Owner

andlabs commented May 5, 2018

Yes, I'll merge those branches shortly.

@andlabs
Copy link
Owner

andlabs commented May 5, 2018

Done.

struct uiImage {
double width;
double height;
// HIMAGELIST images;
Copy link
Owner

@andlabs andlabs May 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a review comment, just a bit of information: I don't think we'll be able to use HIMAGELIST here, since that deals with a set of visually distinct images that have the same pixel sizes, while uiImage deals with a set of visually identical images that has different pixel sizes but the same physical size (for DPI independence). Don't worry about changing anything; I'll work on it later if you don't already have another PR with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I agree. Wasn't planning to go anywhere further with HIMAGELIST, Just stubbing out enough to get the tables compiling. and threw in the comment while I was in listview mode. I didn't spot the multi-size vs different-images distinction.

@@ -0,0 +1,277 @@
#include "uipriv_windows.hpp"

#include <vector>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This include is already in winapi.hpp, so it's not needed.

@andlabs
Copy link
Owner

andlabs commented May 5, 2018

Huh, does github still not let you see merge conflicts?

@andlabs andlabs mentioned this pull request May 7, 2018
@andlabs
Copy link
Owner

andlabs commented May 7, 2018

I merged the Windows fixes into the table branch now; you may need to pull for that to be reflected.

@bcampbell
Copy link
Contributor Author

I rebased my changes to try and keep the history clean. This is nudging against the outer limits of my git-fu, so fingers crossed :- )

@andlabs
Copy link
Owner

andlabs commented May 7, 2018

Seems to have worked. Will review.

Copy link
Owner

@andlabs andlabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly fine with the functionality this provides; most of these comments are consistency-related. Thanks again!

item.iItem = newIndex;
item.iSubItem = 0; //?
for (auto t : m->tables) {
ListView_InsertItem( t->hwnd, &item );
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency note: don't use the commctrl.h wrappers; use SendMessageW() explicitly.

if (SendMesageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1))
	logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()");

(Note this also checks for errors; a thing I still need to rationalize properly.)

Likewise for consistency, no braces around single-line loop bodies

void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
for (auto t : m->tables) {
ListView_DeleteItem( t->hwnd, oldIndex );
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here.

if (SendMesageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (FALSE))
	logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()");

void uiTableModelRowChanged(uiTableModel *m, int index)
{
for (auto t : m->tables) {
ListView_Update( t->hwnd, index );
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here.

if (SendMesageW(t->hwnd, LVM_UPDATE, (WPARAM) oldIndex, 0) == (LRESULT) (FALSE))
	logLastError(L"error calling LVM_UPDATE in uiTableModelRowDeleted()");

}
}

LV_COLUMN lvc;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency: declarations at the top

More important, though:

a) use LVCOLUMNWLV_COLUMN is technically a deprecated name as of at least Vista if not earlier, and the W is consistency to explicitly use Unicode
b) you should call ZeroMemory(&lvc, sizeof (LVCOLUMNW)) before setting any fields (partially consistency, partially correctness)

lvc.fmt = LVCFMT_LEFT;
lvc.cx = 120; // TODO
lvc.pszText = c->name;
ListView_InsertColumn(c->t->hwnd, lvIndex, &lvc);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (SendMessageW(c->t->hwnd, LVM_INSERTCOLUMN, lvIndex, (LPARAM) (&lvc)) == (LRESULT) (-1))
	logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnAppendTextPart()");

// Don't think that'll cut it when some cells have overlong data (eg
// stupidly long URLs). So for now, just hardcode a minimum:

x = 107; // in line with other controls
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would name these constants somehow, for consistency

}
break;
}
default:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for default: break;

switch (nm->code) {
case LVN_GETDISPINFO:
{
NMLVDISPINFO* di = (NMLVDISPINFO*)nm;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style consistency:

		NMLVDISPINFO *di = (NMLVDISPINFO *) nm;	
		LVITEM *item = &(di->item); 

Note that the rest of this function isn't consistent either, but we can worry about that later.

uiTableModelColumnType typ = (*mh->ColumnType)(mh,t->model,mcol);
if (typ == uiTableModelColumnString) {
void* data = (*(mh->CellValue))(mh, t->model, row, mcol);
int n = MultiByteToWideChar(CP_UTF8, 0, (const char*)data, -1, item->pszText, item->cchTextMax);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a problem. It should be using toUTF16(), but I forget what the behavior is if you overwrite the pszText pointer with your own... or when it's safe to free. Just mark this as a TODO; I'll probably resolve it a different way.

if (n>=item->cchTextMax) {
item->pszText[item->cchTextMax-1] = L'\0';
}
} else if (typ == uiTableModelColumnInt) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note: this won't be used for strings, but will be for progressbars. I will need to explicitly document that... Mark that as a TODO for now, anyway; this code can stay for now.

@bcampbell
Copy link
Contributor Author

Just a quick note to assure you I'm still here ;-) It's been a crazy-busy week or so, so I've not been able to spend any time on this. The feedback is great and all makes sense to me, and I'm aiming to apply it all over the next few days.

@andlabs
Copy link
Owner

andlabs commented May 14, 2018

Okay; good luck with everything else going on!

This uses the win32 common controls listview to implement uiTable.
There are limitations:
 - It supports only a single TextPart per column.
 - ImagePart, CheckboxPart and ProgessBarPart are not implemented.
 - There is no support for cell coloring.
 - Cell editing is not implemented.
Some of these will be very hard to support using the standard
common control listview, and probably require an entire custom
listview.
Copy link
Owner

@andlabs andlabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly good to go; just a few minor stuff to fix up first. Thanks again!

I'll talk more about parts stuff on the main issue.

@@ -132,6 +132,8 @@ uiBox *makePage16(void)

uiTableSetRowBackgroundColorModelColumn(t, 3);

uiTableAppendTextColumn(t, "Numbers", 8);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in another comment, the integer column type is for progress-bar parts. not for text strings. I don't remember what happens with other platforms though, so keep this around and I'll remove it if it causes issues.

However, this does raise the question of whether to provide helpers for displaying numbers as text. That might be outside the scope of libui, but it's still an interesting question...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that the type of the cell used in the table display is independent from datatype of the column in the model. While most mappings between the data and display types are clearly pretty bonkers (eg colour => progress bar doesn't sound too useful), it doesn't seem unreasonable for the table to have a good stab at the sane ones (one?). Particularly the numeric => text mapping.
It's a pretty common case to want to display numeric data in a table, so forcing the client app to present it's numeric data as text when the model already has perfectly good support for numeric data seems like a recipe for confusion...


//static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height);

struct uiTable;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this; ui_table.h already does this.

@@ -0,0 +1,295 @@
#include "uipriv_windows.hpp"

//static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this; ui_windows.h already does this.

ZeroMemory(&item, sizeof (LVITEMW));
item.mask = 0;
item.iItem = newIndex;
item.iSubItem = 0; //?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 appears to be correct here. yes, since a subitem index of 0 means the item itself, not any of its subitems. (The leftmost column is not considered as "subitem" because of how listview is basically an extended listbox.)

item.mask = 0;
item.iItem = newIndex;
item.iSubItem = 0; //?
for (auto t : m->tables) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style consistency nit: no need for braces here since the body is one statement.


void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)
{
for (auto t : m->tables) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style consistency nit: no need for braces here since the body is one statement.

int lvIndex = 0;
LVCOLUMNW lvc;

if (c->modelColumn >=0) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style consistency nits: space after >=, no need for braces here since the body is one statement. I'll stop this second one for the rest of the review, but it still applies.

*width = x;
*height = y;
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style consistency nit applied to everything: use only a single blank line between functions. not two.

// Don't think that'll cut it when some cells have overlong data (eg
// stupidly long URLs). So for now, just hardcode a minimum:
#define tableMinWidth 107 /* in line with other controls */
#define tableMinHeight (14*3) /* header + 2 lines (roughly) */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be a way to use LVM_GETHEADER to get the necessary size of the header control, perhaps combined with HDM_LAYOUT, but I'm not sure how this cooperates with listview controls. Mark this as a TODO for now.

SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE,
(WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP),
(LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP));
// TODO: try LVS_EX_AUTOSIZECOLUMNS
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this above the SendMessage() call.

@bcampbell
Copy link
Contributor Author

OK, I think that addresses them all. Let me know if I missed any!

Copy link
Owner

@andlabs andlabs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last comment.

@@ -130,7 +119,7 @@ void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int e

void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable)
{
// TODO
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep this as a TODO for now, so that it shows up in my occasional grep TODOs and thus isn't forgotten.

@andlabs
Copy link
Owner

andlabs commented May 29, 2018

All right; thanks! I'll merge this into table now at last, though there are a few other minor changes and one last runthrough before I merge this into master. Thanks a lot again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants